from simpy import Environment
from simpy import Resource, Store

from typings import Pos3, Pos2, Pos1


class Goods(object):
    """
    货物
    """
    name: str = None
    no: str = None
    shelf_name: str = None
    pos: Pos3 = None

    def __init__(self, name: str, no: str, shelf_name: str, pos: Pos3):
        self.name = name
        self.no = no
        self.shelf_name = shelf_name
        self.pos = pos

    def __str__(self):
        return f'Goods(name={self.name}, no={self.no}, pos={self.pos})'

    def __repr__(self):
        return self.__str__()


class Instruction(object):
    """
    操作指令
    """
    no: str = None
    pos: Pos3 = None

    def __init__(self, no, pos):
        self.no = no
        self.pos = pos


class PickCar(Resource):
    """
    取货车
    """
    name: str = None
    pos: Pos2 = (0, 0)

    def __init__(self, env: Environment, name: str, pos: Pos2 = (0, 0)):
        """
        初始化 Pick Car
        :param env: Env
        :param name: 名称
        :param pos: 初始位置
        :return: PickCar Resource
        """
        super().__init__(env)  # 车数量 (暂时仅能为1)
        self.name = name
        self.pos = pos

    def __str__(self):
        return f'[PickCar](name={self.name}, pos={self.pos})'

    def move_to(self, ins: Instruction):
        """
        取货车移动
        :param ins: 取物指令
        :return:
        """
        pos = ins.pos[:2]
        step = abs(self.pos[0] - pos[0]) + abs(self.pos[1] - pos[1])
        yield self._env.timeout(step)
        print(f'[{ins.no}][PickCar] move from: {self.pos} to: {pos} [{self._env.now}]')
        self.pos = pos

    def reverse(self, ins: Instruction):
        """
        位置调换
        TODO：货物位于内侧，进行位置调换???
        - 0: 无效调换
        - >0: 需要进行调换位置
        :param ins: 取物指令
        :return:
        """
        pos = ins.pos[2]
        if pos > 0:
            yield self._env.timeout(pos)
            # TODO: 货物移动副作用??? 原货物位置被移动
            print(f'[{ins.no}][PickCar] reverse pos: {pos} [{self._env.now}]')


class TransportCar(Resource):
    """
    运货车
    """
    name: str = None
    pos: Pos1 = 0

    def __init__(self, env: Environment, name: str, pos: Pos1 = 0):
        super().__init__(env)
        self.name = name
        self.pos = pos

    def __str__(self):
        return f'[TransportCar](name={self.name}, pos={self.pos})'


class Shelf(Resource):
    """
    货架
    """
    # 编号
    no: str = None
    # 名称
    name: str = None
    # 规格 todo: 假定规格为 10*10 且每个格子存储1个货物
    size: Pos3 = (10, 10, 1)
    # 存储明细
    items_details: list[list[list[Goods]]] = None
    # 存储
    items: Store = None
    # 货物取用车
    pick_car: PickCar = None
    # 货物运输车
    transport_car: TransportCar = None

    def __init__(self, env: Environment, no: str, name: str, pick_pos: Pos2, transport_pos: Pos1):
        super().__init__(env)
        self.no = no
        self.name = name
        self.__init_pick_car(pick_pos)
        self.__init_transport_car(transport_pos)

    def __init_pick_car(self, pick_pos: Pos2) -> None:
        """
        初始化 取货车
        :param pick_pos: 默认位置
        :return:
        """
        pick_name = f'{self.name}-取货车'
        self.pick_car = PickCar(self._env, name=pick_name, pos=pick_pos)

    def __init_transport_car(self, transport_pos: Pos1) -> None:
        """
        初始化 运货车
        :param transport_pos:
        :return:
        """
        transport_name = f'{self.name}-运货车'
        self.transport_car = TransportCar(self._env, transport_name, pos=transport_pos)

    def init_items(self, goods: list[list[list[Goods]]], size: int) -> None:
        """
        初始化 货柜货物
        :param goods:
        :param size:
        :return:
        """
        self.items_details = goods
        self.items = Store(self._env, size)

    def get_item(self, ins: Instruction):
        """
        取货物
        需要根据pos 计算大概耗时
        :param ins: 取货指令
        :return: item货物
        """
        # 校验 存在货物、取物车、运输车
        assert self.items
        assert self.pick_car
        assert self.transport_car
        #
        pos: Pos3 = ins.pos
        assert self.__check_item_pos(ins)
        print(f'[{ins.no}][PickGoods] 等待取货物: {pos} [{self._env.now}]')
        # 请求 取货车 资源
        with self.pick_car.request() as req:
            yield req
            # 移动 取货车
            yield self._env.process(self.pick_car.move_to(ins))
            # 翻转 货物???
            yield self._env.process(self.pick_car.reverse(ins))
            # 获取当前货物信息
            goods = self.get_item_detail(pos)
            print(f'[{ins.no}][PickGoods] 获取当前货物: {goods} [{self._env.now}]')
            # 这里需要返回一个事件
            return goods

    def __check_item_pos(self, ins: Instruction) -> bool:
        """
        校验 货物Item 位置是否正常
        :param ins: Instruction
        :return: Goods 信息
        """
        pos = ins.pos
        if pos[0] > self.size[0]:
            print(f'[{ins.no}][Error] 层级超限')
            return False
        if pos[1] > self.size[1]:
            print(f'[{ins.no}][Error] 列级超限')
            return False
        if pos[2] > self.size[2]:
            print(f'[{ins.no}][Error] 深级超限')
            return False
        print(f'[{ins.no}][Debug] 货物位置正常 pos: {pos} [{self._env.now}]')
        return True

    def get_item_detail(self, pos: Pos3) -> Goods:
        """
        获取 某位置 货物信息
        :param pos:
        :return:
        """
        item = self.items_details[pos[0]][pos[1]][pos[2]]
        return item
